# -*- coding: utf-8 -*-

"""Unimail
@version: 0.14
@license: GPL 3.0 (see "license" file)
@author: Jose A. Jimenez
@contact: jajimc@gmail.com"""

import os
import pickle
import paths

class Settings:
    """Class that represents application settings."""

    def __init__(self, emailAddress, emailName,
    imapServer, imapSsl, imapPort, imapUserId, imapUserPassword, downloadRateMax, inboxDirPath,
    smtpServer, smtpTls, smtpPort, smtpUserId, smtpUserPassword, sendRateMax, partSize, partWaitTime, randomWaitTime):
        """Class constructor.
        @param emailAddress: E-mail address.
        @type emailAddress: C{str}
        @param emailName: E-mail user full name.
        @type emailName: C{str}
        @param imapServer: IMAP host address.
        @type imapServer: C{str}
        @param imapSsl: Specifies if SSL is needed.
        @type imapSsl: C{bool}
        @param imapPort: IMAP host port to connect. 
        @type imapPort: C{int}
        @param imapUserId: IMAP user ID.
        @type imapUserId: C{str}
        @param imapUserPassword: IMAP user password.
        @type imapUserPassword: C{str}
        @param downloadRateMax: Maximum download rate in Kbps.
        @type downloadRateMax: C{int}
        @param inboxDirPath: Inbox folder directory. This is the absolute path where to save downloaded files.
        @type inboxDirPath: C{str}
        @param smtpServer: SMTP host address.
        @type smtpServer: C{str}
        @param smtpTls: Specifies if TLS is needed.
        @type smtpTls: C{bool}
        @param smtpPort: SMTP host port to connect. 
        @type smtpPort: C{int}
        @param smtpUserId: SMTP user ID.
        @type smtpUserId: C{str}
        @param smtpUserPassword: SMTP user password.
        @type smtpUserPassword: C{str}
        @param sendRateMax: Maximum sending rate in Kbps.
        @type sendRateMax: C{int}
        @param partSize: File part size in Mb.
        @type partSize: C{int}
        @param partWaitTime: Time in seconds to wait until sending a file part.
        @type partWaitTime: C{int}
        @param randomWaitTime: Specifies if the time to wait until sending a file part must be a random integer from 0 to C{partWaitTime}.
        @type randomWaitTime: C{bool}"""

        self.set_email_address(emailAddress)
        self.set_email_name(emailName)

        self.set_imap_server(imapServer)
        self.set_imap_ssl_needed(imapSsl)
        self.set_imap_port(imapPort)
        self.set_imap_user_id(imapUserId)
        self.set_imap_user_password(imapUserPassword)
        self.set_download_rate_max(downloadRateMax)
        self.set_inbox_dir_path(inboxDirPath)

        self.set_smtp_server(smtpServer)
        self.set_smtp_tls_needed(smtpTls)
        self.set_smtp_port(smtpPort)
        self.set_smtp_user_id(smtpUserId)
        self.set_smtp_user_password(smtpUserPassword)
        self.set_send_rate_max(sendRateMax)
        self.set_part_size(partSize)
        self.set_part_wait_time(partWaitTime)
        self.set_random_wait_time(randomWaitTime)
        
    def get_email_address(self):
        """rtype: C{str}"""
        return self.__emailAddress
        
    def get_email_name(self):
        """rtype: C{str}"""
        return self.__emailName
        
    def get_imap_server(self):
        """rtype: C{str}"""
        return self.__imapServer
        
    def is_imap_ssl_needed(self):
        """rtype: C{bool}"""
        return self.__imapSsl
    
    def get_imap_port(self):
        """rtype: C{int}"""
        return self.__imapPort
    
    def get_imap_user_id(self):
        """rtype: C{str}"""
        return self.__imapUserId
    
    def get_imap_user_password(self):
        """rtype: C{str}"""
        return self.__imapUserPassword
        
    def get_imap_download_rate_max(self):
        """rtype: C{int}"""
        return self.__downloadRateMax
        
    def get_inbox_dir_path(self):
        """rtype: C{str}"""
        return self.__inboxDirPath
    
    def get_smtp_server(self):
        """rtype: C{str}"""
        return self.__smtpServer
        
    def is_smtp_tls_needed(self):
        """rtype: C{bool}"""
        return self.__smtpTls
    
    def get_smtp_port(self):
        """rtype: C{int}"""
        return self.__smtpPort
    
    def get_smtp_user_id(self):
        """rtype: C{str}"""
        return self.__smtpUserId
    
    def get_smtp_user_password(self):
        """rtype: C{str}"""
        return self.__smtpUserPassword
    
    def get_smtp_send_rate_max(self):
        """rtype: C{int}"""
        return self.__sendRateMax
        
    def get_part_size(self):
        """rtype: C{int}"""
        return self.__partSize
        
    def get_part_wait_time(self):
        """rtype: C{int}"""
        return self.__partWaitTime
        
    def is_random_wait_time(self):
        """rtype: C{bool}"""
        return self.__randomWaitTime
        
    def set_email_address(self, address):
        """@type address: C{str}
        @raise Exception: An exception of type L{Exception} is raised if L{address} value is not C{None} 
        and its type is not C{string}."""
        if address == None:
            self.__address = ""
        elif type(address) != str:
            raise Exception("'address' must be a string or None.")
        else:
            self.__emailAddress = address
            
    def set_email_name(self, name):
        """@type address: C{str}
        @raise Exception: An exception of type L{Exception} is raised if L{name} value is not C{None} 
        and its type is not C{string}."""
        if name == None:
            self.__name = ""
        elif type(name) != str:
            raise Exception("'name' must be a string or None.")
        else:
            self.__emailName = name
        
    def set_imap_server(self, imapServer):
        """@type imapServer: C{str}
        @raise Exception: An exception of type L{Exception} is raised if L{imapServer} value is not C{None} 
        and its type is not C{string}."""
        if imapServer == None:
            self.__imapServer = ""
        elif type(imapServer) != str:
            raise Exception("'imapServer' must be a string or None.")
        else:
            self.__imapServer = imapServer
            
    def set_imap_ssl_needed(self, imapSsl):
        """@type imapSsl: C{bool}
        @raise Exception: An exception of type L{Exception} is raised if L{imapSsl} type is not C{bool}."""
        if imapSsl == None or type(imapSsl) != bool:
            raise Exception("'imapSsl' must be a boolean.")
        else:
            self.__imapSsl = imapSsl
            
    def set_imap_port(self, imapPort):
        """@type imapPort: C{int}
        @raise Exception: An exception of type L{Exception} is raised if L{imapPort} type is not C{int}."""
        if imapPort == None or type(imapPort) != int:
            raise Exception("'imapPort' must be an integer.")
        else:
            self.__imapPort = imapPort
        
    def set_imap_user_id(self, imapUserId):
        """@type imapUserId: C{str}
        @raise Exception: An exception of type L{Exception} is raised if L{imapUserId} value is not C{None} 
        and its type is not C{string}."""
        if imapUserId == None:
            self.__imapUserId = ""
        elif type(imapUserId) != str:
            raise Exception("'imapUserId' must be a string or None.")
        else:
            self.__imapUserId = imapUserId
        
    def set_imap_user_password(self, imapUserPassword):
        """@type imapUserPassword: C{str}
        @raise Exception: An exception of type L{Exception} is raised if L{imapUserPassword} value is not C{None} 
        and its type is not C{string}."""
        if imapUserPassword == None:
            self.__imapUserPassword = ""
        elif type(imapUserPassword) != str:
            raise Exception("'imapUserPassword' must be a string or None.")
        else:
            self.__imapUserPassword = imapUserPassword
        
    def set_download_rate_max(self, downloadRateMax):
        """@type downloadRateMax: C{int}
        @raise Exception: An exception of type L{Exception} is raised if L{downloadRateMax} type is not C{int} 
        or L{downloadRateMax} is an integer less than 0."""
        if downloadRateMax == None or type(downloadRateMax) != int or downloadRateMax < 0:
            raise Exception("'downloadRateMax' must be an integer equal or greater than 0.")
        else:
            self.__downloadRateMax = downloadRateMax
        
    def set_inbox_dir_path(self, inboxDirPath):
        """@type inboxDirPath: C{str}
        @raise Exception: An exception of type L{Exception} is raised if L{inboxDirPath} value is not C{None} 
        and its type is not C{string}."""
        if inboxDirPath == None:
            self.__inboxDirPath = ""
        elif type(inboxDirPath) != str:
            raise Exception("'inboxDirPath' must be a string or None.")
        else:
            self.__inboxDirPath = inboxDirPath

    def set_smtp_server(self, smtpServer):
        """@type smtpServer: C{str}
        @raise Exception: An exception of type L{Exception} is raised if L{smtpServer} value is not C{None} 
        and its type is not C{string}."""
        if smtpServer == None:
            self.__smtpServer = ""
        elif type(smtpServer) != str:
            raise Exception("'smtpServer' must be a string or None.")
        else:
            self.__smtpServer = smtpServer
            
    def set_smtp_tls_needed(self, smtpTls):
        """@type smtpTls: C{bool}
        @raise Exception: An exception of type L{Exception} is raised if L{smtpTls} type is not C{bool}."""
        if smtpTls == None or type(smtpTls) != bool:
            raise Exception("'smtpTls' must be a boolean.")
        else:
            self.__smtpTls = smtpTls
            
    def set_smtp_port(self, smtpPort):
        """@type smtpPort: C{int}
        @raise Exception: An exception of type L{Exception} is raised if L{smtpPort} type is not C{int}."""
        if smtpPort == None or type(smtpPort) != int:
            raise Exception("'smtpPort' must be an integer.")
        else:
            self.__smtpPort = smtpPort
        
    def set_smtp_user_id(self, smtpUserId):
        """@type smtpUserId: C{str}
        @raise Exception: An exception of type L{Exception} is raised if L{smtpUserId} value is not C{None} 
        and its type is not C{string}."""
        if smtpUserId == None:
            self.__smtpUserId = ""
        elif type(smtpUserId) != str:
            raise Exception("'smtpUserId' must be a string or None.")
        else:
            self.__smtpUserId = smtpUserId
        
    def set_smtp_user_password(self, smtpUserPassword):
        """@type smtpUserPassword: C{str}
        @raise Exception: An exception of type L{Exception} is raised if L{smtpUserPassword} value is not C{None} 
        and its type is not C{string}."""
        if smtpUserPassword == None:
            self.__smtpUserPassword = ""
        elif type(smtpUserPassword) != str:
            raise Exception("'smtpUserPassword' must be a string or None.")
        else:
            self.__smtpUserPassword = smtpUserPassword
        
    def set_send_rate_max(self, sendRateMax):
        """@type sendRateMax: C{int}
        @raise Exception: An exception of type L{Exception} is raised if L{sendRateMax} type is not C{int} 
        or L{sendRateMax} is an integer less than 0."""
        if sendRateMax == None or type(sendRateMax) != int or sendRateMax < 0:
            raise Exception("'sendRateMax' must be an integer equal or greater than 0.")
        else:
            self.__sendRateMax = sendRateMax
        
    def set_part_size(self, partSize):
        """@type partSize: C{int}
        @raise Exception: An exception of type L{Exception} is raised if L{partSize} type is not C{int} 
        or L{partSize} is an integer less than 1."""
        if partSize == None or type(partSize) != int or partSize < 1:
            raise Exception("'partSize' must be an integer equal or greater than 1.")
        else:
            self.__partSize = partSize
        
    def set_part_wait_time(self, partWaitTime):
        """@type partWaitTime: C{int}
        @raise Exception: An exception of type L{Exception} is raised if L{partWaitTime} type is not C{int} 
        or L{partWaitTime} is an integer less than 0."""
        if partWaitTime == None or type(partWaitTime) != int or partWaitTime < 0:
            raise Exception("'partWaitTime' must be an integer equal or greater than 0.")
        else:
            self.__partWaitTime = partWaitTime
        
    def set_random_wait_time(self, randomWaitTime):
        """@type randomWaitTime: C{bool}
        @raise Exception: An exception of type L{Exception} is raised if L{randomWaitTime} type is not C{bool}."""
        if randomWaitTime == None or type(randomWaitTime) != bool:
            raise Exception("'randomWaitTime' must be a boolean.")
        else:
            self.__randomWaitTime = randomWaitTime

    def check(self):
        """Checks if all necessary fields are set up.
        @return: C{True} if all necessary fields are set up. C{False} in other case.
        @rtype: C{bool}"""
        conditions = []
        
        conditions.append(self.__emailAddress != "")
        conditions.append(self.__emailName != "")

        conditions.append(self.__imapServer != "")
        conditions.append(self.__imapUserId != "")
        conditions.append(self.__inboxDirPath != "")
        
        conditions.append(self.__smtpServer != "")
        conditions.append(self.__smtpUserId != "")

        valid = True
        for cond in conditions:
            valid = valid and cond
        
        return valid

class SettingsManager:
    """Manages application settings."""

    def save(self, settings, path):
        """Saves application settings.
        @param settings: Settings to save.
        @type settings: L{Settings}
        @param path: File path where to save L{settings}.
        @type path: C{str}
        @raise Exception: An exception of type L{Exception} is raised if L{settings} type is not L{Settings}.
        @raise Exception: An exception of type L{Exception} is raised if L{path} type is not C{str}.
        @raise Exception: An exception of type L{Exception} is raised if there is any error saving L{settings}."""
        if settings == None or not isinstance(settings, Settings):
            raise Exception("'settings' must be a 'Settings' object.")
        if path == None or type(path) != str or path == "":
            raise Exception("'path' must be a not empty string.")
            
        try:
            outputFile = open(path, "wb")
            pickle.dump(settings, outputFile)
            outputFile.close()
        except:
            raise Exception("Error saving settings.")

    def load(self, path):
        """Loads application settings.
        @return: Settings.
        @rtype: L{Settings}
        @raise Exception: An exception of type L{Exception} is raised if L{path} type is not C{str}.
        @raise Exception: An exception of type L{Exception} is raised if there is any error loading settings."""
        if path == None or type(path) != str or path == "":
            raise Exception("'path' must be a not empty string.")
        if not (os.path.isfile(path) and not os.path.islink(path)):
            raise Exception("'path' file does not exist.")
            
        try:
            inputFile = open(path, "rb")
            settings = pickle.load(inputFile)
            inputFile.close()
        except:
            raise Exception("Error loading settings.")

        return settings
 
#ToDo: Check default ports
def get_default_settings():
    """Gets application default settings.
    @return: Default settings.
    @rtype: L{Settings}."""
    return Settings("", "", "", True, 993, "", "", 0, paths.DEFAULT_INBOX_DIR, "", True, 995, "", "", 0, 15, 0, False)
    
#ToDo
def get_known_settings(serviceId):
    """
    settings = None
    
    try:
        servicesFile = open("services.conf", "r")
        
        for line in servicesFile.readline():
            if line != "":
                
        
        servicesFile.close()
    except:
        settings = get_default_settings()
        
    return settings
    """
    return get_default_settings()
    
def save_settings(settings):
    """Saves application settings to default location.
    @param settings: Settings to save.
    @type settings: L{Settings}
    @raise Exception: An exception of type L{Exception} is raised if L{settings} type is not L{Settings}."""
    if settings == None or not isinstance(settings, Settings):
        raise Exception("'settings' must be a 'Settings' object.")
            
    try:
        SettingsManager().save(settings, paths.SETTINGS_FILE)
        paths.check_paths()
    except:
        raise
     
#ToDo   
def get_service_param(param):
    pass
    #if type(param) != str or param == ""
    
def load_settings():
    """Loads application settings from default location. It returns default settings if an error occurs."""
    try:
        settings = SettingsManager().load(paths.SETTINGS_FILE)
    except:
        settings = get_default_settings()
    return settings
